library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;

entity MemoryArbiter is
	port
	(
		-- clk (neg-edge triggered), active-low reset
		clk, nReset			: in	STD_LOGIC;
		-- memory signals (in order)
		memAddr					:	out	STD_LOGIC_VECTOR(15 downto 0); -- mem address being read/written
		memWriteData		: out	STD_LOGIC_VECTOR(31 downto 0); -- mem data to be written
		memWEN					: out	STD_LOGIC; -- write enable
		memREN					: out	STD_LOGIC; -- read enable
		memReadData			: in	STD_LOGIC_VECTOR(31 downto 0); -- mem data to be read
		memState				: in	STD_LOGIC_VECTOR(1 downto 0); -- state of mem
		-- icache signals (in order)
		icacheWait			: out	STD_LOGIC; -- icache must wait if 1
		icacheREN				:	in	STD_LOGIC; -- icache read request
		icacheAddr			: in	STD_LOGIC_VECTOR(31 downto 0); -- icache address being read
		icacheReadData	: out	STD_LOGIC_VECTOR(31 downto 0); -- icache data to be read
		-- dcache signals (in order)
		dcacheREN				: in	STD_LOGIC; -- dcache read request
		dcacheWEN				: in	STD_LOGIC; -- dcache write request
		dcacheWait			:	out	STD_LOGIC; -- dcache must wait if 1
		dcacheAddr			: in	STD_LOGIC_VECTOR(31 downto 0); -- dcache address being read
		dcacheReadData	: out	STD_LOGIC_VECTOR(31 downto 0); -- dcache data to be read
		dcacheWriteData	: in	STD_LOGIC_VECTOR(31 downto 0) -- dcache data to be written
	);
end MemoryArbiter;

architecture MemoryArbiter_arch of MemoryArbiter is

	type state_type is (idle, daccess, iaccess);
	signal state, nextstate : state_type;

begin

	process( nReset, clk )
	begin
		if( nReset='0' ) then
			state <= idle;
		elsif( falling_edge(clk) ) then
			state <= nextstate;
		end if;
	end process;

	process( memState, icacheREN, dcacheREN, dcacheWEN, icacheAddr, dcacheAddr, dcacheWriteData, memReadData )
	begin

		case state is
			when idle =>
						-- defaults
						nextstate <= idle;
						icacheWait <= '0';
						dcacheWait <= '0';
						icacheReadData <= x"00000000";
						dcacheReadData <= x"00000000";
						memAddr <= x"0000";
						memWriteData <= x"00000000";
						memREN <= '0';
						memWEN <= '0';
						-- when inputs detected
						if( dcacheREN='1' or dcacheWEN='1' ) then
									icacheWait <= '1';
									dcacheWait <= '1';
									memREN <= dcacheREN;
									memWEN <= dcacheWEN;
									memAddr <= dcacheAddr(15 downto 0);
									dcacheReadData <= memReadData;
									memWriteData <= dcacheWriteData;
									if( memState="01" ) then -- accessing mem state
										nextstate <= daccess;
										dcacheWait <= '1';
										icacheWait <= '1';
									elsif( memState="10" ) then -- mem ready state (zero access time)
										nextstate <= idle;
										dcacheWait <= '0';
										icacheWait <= '1';
									elsif( memState="00" ) then -- mem free state (won't last long b/c REN+WEN are connected)
										nextstate <= idle;
										dcacheWait <= '1';
										icacheWait <= '1';
									else -- error state
										-- this likely means dcache REN and WEN are both 1
										nextstate <= idle;
										dcacheWait <= '0'; -- set to zero b/c of inf loop hazard
										icacheWait <= '0';
									end if;
						elsif( icacheREN='1' ) then
									dcacheWait <= '1';
									icacheWait <= '1';
									memREN <= icacheRen;
									memWEN <= '0';
									memAddr <= icacheAddr(15 downto 0);
									icacheReadData <= memReadData;
									memWriteData <= x"00000000";
									if( memState="01" ) then -- accessing mem state
												nextstate <= iaccess;
												dcacheWait <= '1';
												icacheWait <= '1';
									elsif( memState="10" ) then -- mem ready state (zero access time)
												nextstate <= idle;
												icacheWait <= '0';
												dcacheWait <= '1';
									elsif( memState="00" ) then -- mem free state (won't last long b/c REN is connected)
												nextstate <= idle;
												icacheWait <= '1';
												dcacheWait <= '1';
									else -- error state (unlikely)
												nextstate <= idle;
												icacheWait <= '0'; -- set to zero b/c of inf loop hazard
												dcacheWait <= '0';
									end if;
						end if;
			when daccess =>
						dcacheWait <= '1';
						icacheWait <= '1';
						nextstate <= daccess;
						memREN <= dcacheREN;
						memWEN <= dcacheWEN;
						memAddr <= dcacheAddr(15 downto 0);
						dcacheReadData <= memReadData;
						memWriteData <= dcacheWriteData;
						if( memState="10" ) then -- mem ready now
									dcacheWait <= '0';
									nextstate <= idle;
						elsif( memState="11" or memState="00" ) then -- some kind of error
									nextstate <= idle; -- just in case of inf loop, go back to idle
						end if;
			when iaccess =>
						icacheWait <= '1';
						dcacheWait <= '1';
						nextstate <= iaccess;
						memREN <= icacheRen;
						memWEN <= '0';
						memAddr <= icacheAddr(15 downto 0);
						icacheReadData <= memReadData;
						memWriteData <= x"00000000";
						if( memState="10" ) then -- mem ready now
									icacheWait <= '0';
									nextstate <= idle;
						elsif( memState="11" or memState="00" ) then -- some kind of error
									nextstate <= idle; -- just in case of inf loop, go back to idle
						end if;
			when others =>
					-- weird stuff
		end case;

	end process;
	
end MemoryArbiter_arch;
